package com.aqie.medium.backtrack;

import java.util.*;

/**
 * 47 全排列2 ： 可包含重复数字的序列,返回所有不重复的全排列
 * todo
 */
public class PermuteUnique {
    private List<List<Integer>> res = new ArrayList<>();
    private boolean[] used;

    private void findPermuteUnique(int[] nums, int depth, Stack<Integer> stack) {
        if (depth == nums.length) {
            res.add(new ArrayList<>(stack));
            return;
        }
        for (int i = 0; i < nums.length; i++) {
            if (!used[i]) {
                // 修改 2：因为排序以后重复的数一定不会出现在开始，故 i > 0
                // 和之前的数相等，并且之前的数还未使用过，只有出现这种情况，才会出现相同分支
                // 这种情况跳过即可
                if (i > 0 && nums[i] == nums[i - 1] && !used[i - 1]) {
                    continue;
                }
                used[i] = true;
                stack.add(nums[i]);
                findPermuteUnique(nums, depth + 1, stack);
                stack.pop();
                used[i] = false;
            }
        }
    }

    /**
     * 回溯 +剪枝
     * @param nums
     * @return
     */

    public List<List<Integer>> permuteUnique(int[] nums) {
        int len = nums.length;
        if (len == 0) {
            return res;
        }
        // 修改 1：首先排序，之后才有可能发现重复分支
        Arrays.sort(nums);
        used = new boolean[len];
        findPermuteUnique(nums, 0, new Stack<>());
        return res;
    }



    public List<List<Integer>> permuteUnique2(int[] nums) {
        int len = nums.length;
        Map<Integer,Integer> map = new HashMap<Integer,Integer>();
        for(int i=0; i<nums.length; i++){
            map.put(nums[i],map.getOrDefault(nums[i],0)+1);
        }
        get(map,new ArrayList<Integer>(),null);
        return res;
    }

    public void get(Map<Integer,Integer> map, List<Integer> list, Integer i){
        if(i!=null)remove(map,i);
        if(map.isEmpty()){
            res.add(list);
            return;
        }
        for(Integer ii: map.keySet()){
            List<Integer> ll = new ArrayList<Integer>();
            ll.add(ii);
            ll.addAll(list);
            get(new HashMap<Integer,Integer>(map),ll,ii);
        }
    }
    public void remove(Map<Integer,Integer> map,Integer i){
        if(map.get(i)>1){
            map.put(i,map.get(i)-1);
        }else{
            map.remove(i);
        }

    }


}
